# coding: utf-8
import os

import CurveScene
import maya.cmds as cmds  # pylint: disable=E0401
import PSOFT.PencilLineGuiUtils as plgu
import CurveEditorBoilerplate as cep
import Pcl4Curve as pc
from PSOFT.PencilLineGuiUtils import getLocalizedString
from PSOFT.vendor.Qt_py.Qt import QtCore, QtGui, QtWidgets
from numedit import pNumEdit

mayaApi = cmds.about(api=True)
if mayaApi >= 201700:
    import shiboken2 as shiboken  # pylint: disable=E0401
else:
    import shiboken  # pylint: disable=E0401

_ICON_LINEAR = "..\\..\\..\\icons\\BezierCurve_LinearButton.png"
_ICON_AUTO_BEZIER = "..\\..\\..\\icons\\BezierCurve_AutoBezierButton.png"
_ICON_BEZIER = "..\\..\\..\\icons\\BezierCurve_BezierButton.png"
_ICON_BREAK = "..\\..\\..\\icons\\BezierCurve_BreakButton.png"
_ICON_REVERSE = "..\\..\\..\\icons\\BezierCurve_ReverseButton.png"
_ICON_SYMMETRY = "..\\..\\..\\icons\\BezierCurve_SymmetrizeButton.png"

CURRENT_PATH = os.path.dirname(__file__)


class CurveView(QtWidgets.QGraphicsView):
    def __init__(self, parent=None):
        super(CurveView, self).__init__(parent)

        self._scene = CurveScene.PencilCurveScene(self)
        self.setScene(self._scene)
        self.setRenderHint(QtGui.QPainter.Antialiasing)
        self.setTransform(QtGui.QTransform(1, 0, 0, -1, 0, 0))
        self.setSceneRect(0, 0,
                          cep.VIEW_WIDTH + cep.VIEW_MERGIN_X * 2,
                          cep.VIEW_HEIGHT + cep.VIEW_MERGIN_BOTTOM + cep.VIEW_MERGIN_TOP)
        self.setFrameRect(
            QtCore.QRect(0, 0,
                         cep.VIEW_WIDTH + cep.VIEW_MERGIN_X * 2,
                         cep.VIEW_HEIGHT + cep.VIEW_MERGIN_BOTTOM + cep.VIEW_MERGIN_TOP))
        self.setDragMode(QtWidgets.QGraphicsView.RubberBandDrag)

    # Returns a list of the Maya indices for the selected control points
    def getSelectedMayaIndices(self):
        return self._scene.getSelectedMayaIndices()

    # Selects the control points matching the specified Maya indices
    def setSelectedMayaIndices(self, idx):
        self._scene.setSelectedMayaIndices(idx)

    # Deselects all control points
    def allControlPointsDeselect(self):
        self._scene.allControlPointsDeselect()

    def resizeEvent(self, event):
        super(CurveView, self).resizeEvent(event)
        self.setSceneRect(0, 0,
                          event.size().width(),
                          cep.VIEW_HEIGHT + cep.VIEW_MERGIN_BOTTOM + cep.VIEW_MERGIN_TOP)
        vwidth = max(event.size().width() - cep.VIEW_MERGIN_X*2, 0.0)

        self._scene.background.viewWidth = vwidth
        self._scene.controlPoints.viewWidth = vwidth

    def event(self, event):
        if event.type() == QtCore.QEvent.EnabledChange:
            self.setEnabledGraphics(self.isEnabled())
        return super(CurveView, self).event(event)

    def setEnabledGraphics(self, arg):
        for item in self._scene.items():
            item.setEnabled(arg)


class CurveWidget(QtWidgets.QWidget):
    # Copy buffer for points data
    _copyData = None

    def __init__(self, parent=None):
        super(CurveWidget, self).__init__(parent)

        def defaultCallback0():
            pass

        def defaultCallback1(p1):
            pass

        self.copyCallback = defaultCallback0
        self.pasteCallback = defaultCallback1

        self.curveView = CurveView(parent=self)

        # 右側のボタン
        buttonSize = QtCore.QSize(24, 24)
        buttonLayout = QtWidgets.QHBoxLayout()

        def applyHandleType(htype):
            return lambda: self.curveView.scene().applyHandleType(htype)

        def makeButton(iconFileName, tooltip, slot):
            button = QtWidgets.QPushButton()
            button.setStyleSheet(
                'QPushButton:pressed { \
                        background-color: \
                        (40,40,40); \
                    }'
                + 'QPushButton { \
                        background-color:transparent;\
                        border:1px solid black; \
                        border-radius: 2px; \
                    }'
                + 'QPushButton:disabled { \
                        border:none; \
                        background-color: \
                        rgb(78, 78, 78); \
                    }'
                + 'QPushButton:hover:!pressed { \
                        background-color: \
                        rgba(255, 255, 255, 25); \
                    }')
            button.clicked.connect(slot)
            iconPath = os.path.join(CURRENT_PATH, iconFileName)
            button.setIcon(QtGui.QIcon(QtGui.QPixmap(iconPath)))
            button.setIconSize(buttonSize)
            button.setToolTip(tooltip)
            buttonLayout.addWidget(button)
            return button

        buttonLayout.setContentsMargins(0, 0, 0, 0)
        buttonLayout.setSpacing(3)
        makeButton(_ICON_LINEAR,
                   getLocalizedString("kCurveEditorToolTipLinear"),
                   applyHandleType(pc.HANDLE_LINEAR))
        makeButton(_ICON_AUTO_BEZIER,
                   getLocalizedString("kCurveEditorToolAutoBezier"),
                   applyHandleType(pc.HANDLE_SPLINE))
        makeButton(_ICON_BEZIER,
                   getLocalizedString("kCurveEditorToolBezier"),
                   applyHandleType(pc.HANDLE_BEZIER))
        makeButton(_ICON_BREAK,
                   getLocalizedString("kCurveEditorToolBreakHandle"),
                   applyHandleType(pc.HANDLE_BREAK))
        buttonLayout.addSpacerItem(
            QtWidgets.QSpacerItem(10, 10,
                                  QtWidgets.QSizePolicy.Fixed,
                                  QtWidgets.QSizePolicy.Fixed))
        makeButton(_ICON_REVERSE,
                   getLocalizedString("kCurveEditorToolFlipCurve"),
                   self.curveView.scene().reverseCurve)
        self.symmetrizeButton = makeButton(
                    _ICON_SYMMETRY,
                    getLocalizedString("kCurveEditorToolSymmetrize"),
                    lambda : (
                        cmds.undoInfo(openChunk=True),
                        self.curveView.scene().symmetrizeCurve(),
                        cmds.undoInfo(closeChunk=True)
                    ))
        # self.symmetrizeButton.setEnabled(False)

        buttonLayout.addSpacerItem(
            QtWidgets.QSpacerItem(10, 10,
                                  QtWidgets.QSizePolicy.Expanding,
                                  QtWidgets.QSizePolicy.Fixed))

        # X, Y表示
        xLabel = QtWidgets.QLabel("X")
        xLabel.setMaximumWidth(20)
        yLabel = QtWidgets.QLabel("Y")
        yLabel.setMaximumWidth(20)
        self.xNumEdit = pNumEdit()
        self.xNumEdit.setMaximumWidth(50)
        self.xNumEdit.setMaxLength(10)
        self.xNumEdit.targerkeys = [QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return]
        self.yNumEdit = pNumEdit()
        self.yNumEdit.setMaximumWidth(50)
        self.yNumEdit.setMaxLength(10)
        self.yNumEdit.targerkeys = [QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return]

        # Copy paste buttons
        cpButtonHeight = 25
        self.copyButton = QtWidgets.QPushButton()
        self.copyButton.setText(plgu.getLocalizedString('kCopyButton'))
        self.copyButton.setMinimumHeight(cpButtonHeight)
        self.copyButton.setSizePolicy(
            QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
        self.copyButton.clicked.connect(lambda: self.copy())
        self.pasteButton = QtWidgets.QPushButton()
        self.pasteButton.setText(plgu.getLocalizedString('kPasteButton'))
        self.pasteButton.setMinimumHeight(cpButtonHeight)
        self.pasteButton.setSizePolicy(
            QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
        self.pasteButton.clicked.connect(lambda: self.paste())

        # Disable the paste button if the copy buffer is empty.
        if CurveWidget._copyData is None:
            self.pasteButton.setEnabled(False)

        bottomLayout = QtWidgets.QHBoxLayout()
        bottomLayout.addWidget(xLabel)
        bottomLayout.addWidget(self.xNumEdit)
        bottomLayout.addWidget(yLabel)
        bottomLayout.addWidget(self.yNumEdit)
        bottomLayout.addWidget(self.copyButton)
        bottomLayout.addWidget(self.pasteButton)

        curveLayout = QtWidgets.QVBoxLayout()
        curveLayout.setSpacing(3)
        curveLayout.setContentsMargins(0, 0, 0, 0)
        curveLayout.addLayout(buttonLayout)
        curveLayout.addWidget(self.curveView)

        baseLayout = QtWidgets.QVBoxLayout()
        baseLayout.setContentsMargins(0, 0, 0, 0)
        baseLayout.addLayout(curveLayout)
        baseLayout.addLayout(bottomLayout)

        self.curveView._scene.xyTextUpdate.connect(self.setXYValue)
        self.curveView._scene.symButtonUpdate.connect(self.setEnableSymmetrizeButton)
        self.xNumEdit.update.connect(self.editedX)
        self.yNumEdit.update.connect(self.editedY)

        self.setLayout(baseLayout)

    # Returns a list of the Maya indices for the selected control points
    def getSelectedMayaIndices(self):
        return self.curveView.getSelectedMayaIndices()

    # Selects the control points matching the specified Maya indices
    def setSelectedMayaIndices(self, idx):
        self.curveView.setSelectedMayaIndices(idx)

    # Deselects all control points
    def allControlPointsDeselect(self):
        self.curveView.allControlPointsDeselect()

    @QtCore.Slot(float, float)
    def setXYValue(self, xValue, yValue):
        if 0 <= xValue <= 1:
            self.xNumEdit.setText('{0:.3f}'.format(xValue))
        else:
            self.xNumEdit.setText('')
        if 0 <= yValue <= 1:
            self.yNumEdit.setText('{0:.3f}'.format(yValue))
        else:
            self.yNumEdit.setText('')

    @QtCore.Slot(str)
    def editedX(self, text):
        try:
            val = eval(text)
            val = pc.clamp(val, 0, 1)
            self.curveView._scene.controlPoints.setControlPointX(val)
            self.xNumEdit.setText('{0:.3f}'.format(val))
        except Exception:
            pass

    @QtCore.Slot(str)
    def editedY(self, text):
        try:
            val = eval(text)
            val = pc.clamp(val, 0, 1)
            self.curveView._scene.controlPoints.setControlPointY(val)
            self.yNumEdit.setText('{0:.3f}'.format(val))
        except Exception:
            pass

    @QtCore.Slot(bool)
    def setEnableSymmetrizeButton(self, enable):
        self.symmetrizeButton.setEnabled(enable)

    def setMayaCallback(self, cpCreated=None, cpChanged=None, cpDestroyed=None,
                        onCopy=None, onPaste=None):
        self.curveView._scene.setMayaCallback(
            cpCreated=lambda cp: cpCreated(cp),
            cpChanged=lambda cp: cpChanged(cp),
            cpDestroyed=lambda cp: cpDestroyed(cp))

        if onCopy:
            self.copyCallback = onCopy

        if onPaste:
            self.pasteCallback = lambda cpts: onPaste(cpts)

    def addControlPoint(self, mayaIndex, params):
        self.curveView._scene.controlPoints.model.addPoint(mayaIndex, params)
        self.curveView._scene.controlPoints.model.sort()
        self.curveView._scene.controlPoints.update()

    def resetControlPoint(self):
        self.curveView._scene.controlPoints.model.clear()
        self.curveView._scene.controlPoints.update()

    # Handle copy of curve data
    def copy(self):
        CurveWidget._copyData = self.curveView._scene.controlPoints.model.getPointsData()
        self.copyCallback()

    # Handle paste of curve data
    def paste(self):
        if CurveWidget._copyData is None:
            cmds.error("Attempting to paste but Bezier curve copy buffer is empty!")
            return

        # Clear the current selection
        self.allControlPointsDeselect()

        # Call the paste callback (to apply the pasted values to the attribute).
        self.pasteCallback(CurveWidget._copyData)

        # The following would apply the copied values to the UI.
        # However, in practice, applying the values to the attribute
        # by calling the callback above
        # should activate a scriptjob that will force an update of the UI,
        # so we skip this step to avoid updating twice.
        # Re-enable if we use the curve editor with a widget
        # that does not have such a behaviour.
        # self.curveView._scene.controlPoints.model.setPointsData(CurveWidget._copyData)
        # self.curveView._scene.controlPoints.update()

    # Enable the Paste button
    def enablePasteButton(self):
        if shiboken.isValid(self.pasteButton):
            self.pasteButton.setEnabled(True)


class TestDialog(QtWidgets.QDialog):
    def __init__(self, parent=None):
        super(TestDialog, self).__init__(parent)
        self.setWindowTitle("Test")
        layout = QtWidgets.QVBoxLayout()
        self.curve = CurveWidget()
        self.curve.addControlPoint(0, [2, -0.1, 0.5, 0.0, 0.5, 0.1, 0.5, ])
        self.curve.addControlPoint(0, [2,  0.4, 1.0, 0.5, 1.0, 0.6, 1.0, ])
        self.curve.addControlPoint(0, [2,  0.9, 0.5, 1.0, 0.5, 1.1, 0.5, ])
        layout.addWidget(self.curve)
        self.setLayout(layout)
        self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)


if __name__ == '__main__':
    import sys
    app = QtGui.QApplication(sys.argv)

    ui = TestDialog()
    ui.show()
    app.exec_()
